package org.opens.javaskill.jvm.classloader.delegation;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @Date 2020/10/31 16:00
 * @Created by 99126
 */
public class BreakTheParentDelegation {

    public static void main(String[] args) throws ClassNotFoundException {
        SimpleClassLoader classLoader = new SimpleClassLoader();
        classLoader.setDir("E:\\classloader\\classloader3");
        Class<?> aClass = classLoader.loadClass("org.opens.javaskill.jvm.classloader.delegation.SimpleObject");
        System.out.println(aClass.getClassLoader());
    }

}

/**
 * 功能:
 * <p>测试子加载器加载的类与父加载器加载的类不可互转.</p>
 */
class BreakTheParentDelegation2 {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        SimpleClassLoader classLoader = new SimpleClassLoader();
        classLoader.setDir("E:\\classloader\\classloader3");
        Class<?> aClass = classLoader.loadClass("org.opens.javaskill.jvm.classloader.delegation.SimpleObject");
        SimpleObject simpleObject = (SimpleObject) aClass.newInstance();
    }

}

/**
 * 功能:
 * <p>测试同一个类加载器的不同实例加载同一个类时, Class对象是不同的</p>
 */
class BreakTheParentDelegation3 {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        SimpleClassLoader classLoader1 = new SimpleClassLoader();
        SimpleClassLoader classLoader2 = new SimpleClassLoader();
        classLoader1.setDir("E:\\classloader\\classloader3");
        classLoader2.setDir("E:\\classloader\\classloader3");
        Class<?> aClass1 = classLoader1.loadClass("org.opens.javaskill.jvm.classloader.delegation.SimpleObject");
        Class<?> aClass2 = classLoader2.loadClass("org.opens.javaskill.jvm.classloader.delegation.SimpleObject");
        System.out.println(aClass1.hashCode());
        System.out.println(aClass2.hashCode());
    }

}

class SimpleClassLoader extends ClassLoader {

    private static final String DEFAULT_DIR = "E:\\classloader";

    private String dir = DEFAULT_DIR;

    private String classLoaderName;

    public SimpleClassLoader() {
        super();
    }

    public SimpleClassLoader(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

    public SimpleClassLoader(String classLoaderName, ClassLoader parent) {
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    public String getDir() {
        return dir;
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public String getClassLoaderName() {
        return classLoaderName;
    }

    public void setClassLoaderName(String classLoaderName) {
        this.classLoaderName = classLoaderName;
    }

    /**
     * 说明:
     * <pre>
     *     1. 任何类加载器都必须重写这个方法, 因为ClassLoader类的这个方法不重写会直接抛出异常.
     * </pre>
     *
     * @param name 加载的class的路径信息
     * @return 加载类的Class对象, 可以使用这个对象创建对象, 调用函数等;
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String classpath = name.replace(".", "/");
        File classfile = new File(dir, classpath + ".class");
        if (!classfile.exists()) {
            throw new ClassNotFoundException("The classname " + name + " not found");
        }

        byte[] classBytes = loadClassBytes(classfile);
        if (null == classBytes || classBytes.length == 0) {
            throw new ClassNotFoundException("load the class " + name + " failed");
        }

        return this.defineClass(name, classBytes, 0, classBytes.length);
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> clazz = null;
        if (name.startsWith("java.")) {
            try {
                ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
                clazz = systemClassLoader.loadClass(name);
                if (clazz != null) {
                    if (resolve) {
                        resolveClass(clazz);
                    }
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // ignore
            }
        }

        try {
            clazz = findClass(name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        if (clazz == null && this.getParent() != null) {
            ClassLoader parent = this.getParent();
            clazz = parent.loadClass(name);
        }
        return clazz;
    }

    /**
     * 功能:
     * <p>将文件转换为byte[]</p>
     *
     * @param classfile class文件对象
     */
    private byte[] loadClassBytes(File classfile) {
        try (
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                FileInputStream fis = new FileInputStream(classfile);
        ) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            bos.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

